--[[
	Ikarus Unit-Frames Addon	
	released under GNU General Public License version 3 (GPLv3) 
	by McGaerty & Sl4ker
	
	contact: mcgaerty@ikarus-systems.net rom@sl4ker.de	
	download: http://rom.curse.com/downloads/rom-addons/details/ikarus-unitframes.aspx
	
	special thanks to Qwox and novayuna for inspiration and stable basics
]]



	--[[
	
		Ikarus uses a simple formatting code (called IUC), very similar to common BB-Code.
		Keywords are eclosed by brackets (then called 'Tag'), eg "[LEVEL]" will result in the level
		of the corresponding unit to be displayed at the given text position.
		There is no actual limitation to the number of tags used per text, however massive use
		will result in slow processing!
		
		The following tags are supported by now (some of them will work only at certain unit frames):
		
		NAME			The name of the unit
		LVL				The primary class level of the unit
		SLVL				The secondary class level of the unit
		CLASS			The primary class of the unit
		SCLASS			The secondary class of the unit
		CLASS_SYMBOL		The first character of the CLASS string
		SCLASS_SYMBOL		The first character of the SCLASS string
		
		HP				The current HP of the unit
		HP_MAX			The current maximal HP of the unit
		HP_REL			The relative HP of the unit, eg. the current percentage, as a plain integer
		HEALTH			A complete health string: "HP / HP_MAX (HP_REL%)"
		HP_DIFF			The difference between current HP and unit's max HP
		
		MP				The current MP of the unit
		MP_MAX			The current maximal MP of the unit
		MP_REL			The relative MP of the unit, eg. the current percentage, as a plain integer
		MANA			A complete mana string: "MP / MP_MAX (MP_REL%)"
		MANA_TYPE		The type of mana used by the main class
		
		SP				The current skill dependent points of the unit
		SP_MAX			The maximal skill dependent points of the unit
		SKILL			A complete skill string: "SP / SP_MAX (SP%)"
		SKILL_TYPE		The type of mana used by the sub class
		
		TYPE				The type of the unit, one of the following: PLAYER, NPC, MOB, ELITE, BOSS
		TITLE			The unit's title
		STATE			The unit's state: ALive or Dead
		
		TARGET			The name of the unit's target
		UNIT			The frame's unit
		DISTANCE			The distance to the unit
		
		Castbar tags
		SPELL_NAME		The current spell's name
		SPELL_CASTTIME	The total cast time of the current spell (does not include delay)
		SPELL_DURATION	The total cast time of the current spell (includes delay)
		SPELL_SPENT		The time spent to cast the spell so far
		SPELL_LEFT		The time left for completion of the current spell
		SPELL_PROGRESS	The progress in casting the current spell
		SPELL			A complete spell string: "SPELL_SPENT / SPELL_DURATION (SPELL_PROGRESS)"
		
		Game tags
		PING				The most recent ping value
		FPS				The current frame rate
		ZONE_ID			The current zone ID
		ZONE			The name of the current zone
	]]
	
ikarus.iuc.replacements = {};
ikarus.iuc.isInitialised = false;
	
function ikarus.iuc:IsInitialised()
	return self.isInitialised == true;
end;
	
function ikarus.iuc:Initialise()
	if not(self:IsInitialised()) then
		self:RegisterPattern("NAME", ikarus.iuc.NAME);
		self:RegisterPattern("LVL", ikarus.iuc.LVL);
		self:RegisterPattern("SLVL", ikarus.iuc.SLVL);
		self:RegisterPattern("CLASS", ikarus.iuc.CLASS);
		self:RegisterPattern("SCLASS", ikarus.iuc.SCLASS);
		self:RegisterPattern("CLASS_SYMBOL", ikarus.iuc.CLASS_SYMBOL);
		self:RegisterPattern("SCLASS_SYMBOL", ikarus.iuc.SCLASS_SYMBOL);
		
		self:RegisterPattern("HP", ikarus.iuc.HP);
		self:RegisterPattern("HP_MAX", ikarus.iuc.HP_MAX);
		self:RegisterPattern("HP_REL", ikarus.iuc.HP_REL);
		self:RegisterPattern("HEALTH", ikarus.iuc.HEALTH);
		self:RegisterPattern("HP_DIFF", ikarus.iuc.HP_DIFF);
		
		self:RegisterPattern("MP", ikarus.iuc.MP);
		self:RegisterPattern("MP_MAX", ikarus.iuc.MP_MAX);
		self:RegisterPattern("MP_REL", ikarus.iuc.MP_REL);
		self:RegisterPattern("MANA", ikarus.iuc.MANA);
		self:RegisterPattern("MANA_TYPE", ikarus.iuc.MANA_TYPE);
		
		self:RegisterPattern("SP", ikarus.iuc.SP);
		self:RegisterPattern("SP_MAX", ikarus.iuc.SP_MAX);
		self:RegisterPattern("SKILL", ikarus.iuc.SKILL);
		self:RegisterPattern("SKILL_TYPE", ikarus.iuc.SKILL_TYPE);
		
		self:RegisterPattern("TYPE", ikarus.iuc.TYPE);
		self:RegisterPattern("TITLE", ikarus.iuc.TITLE);
		self:RegisterPattern("STATE", ikarus.iuc.STATE);
		self:RegisterPattern("TARGET", ikarus.iuc.TARGET);
		self:RegisterPattern("UNIT", ikarus.iuc.UNIT);
		
		self:RegisterPattern("SPELL_NAME", ikarus.iuc.SPELL_NAME);
		self:RegisterPattern("SPELL_CASTTIME", ikarus.iuc.SPELL_CASTTIME);
		self:RegisterPattern("SPELL_DURATION", ikarus.iuc.SPELL_DURATION);
		self:RegisterPattern("SPELL_SPENT", ikarus.iuc.SPELL_SPENT);
		self:RegisterPattern("SPELL_LEFT", ikarus.iuc.SPELL_LEFT);
		self:RegisterPattern("SPELL_PROGRESS", ikarus.iuc.SPELL_PROGRESS);
		self:RegisterPattern("SPELL", ikarus.iuc.SPELL);
		
		self:RegisterPattern("PING", ikarus.iuc.PING);
		self:RegisterPattern("FPS", ikarus.iuc.FPS);
		self:RegisterPattern("ZONE_ID", ikarus.iuc.ZONE_ID);
		self:RegisterPattern("ZONE", ikarus.iuc.ZONE);
	end;
end;

function ikarus.iuc:RegisterPattern(pattern, replacement)
	if(pattern ~= nil and replacement ~= nil) then
		self.replacements["["..pattern.."]"] = replacement;
	end;
end;

function ikarus.iuc:Process(text, delegate)
	--ikarus.debug:Dump(text);
	if(type(text) == "string") then
		ikarus.iuc.delegate = delegate;
		return string.gsub(text, "%[[%w_]+%]", ikarus.iuc.Replace);
	else
		return "";
	end;
end;

function ikarus.iuc.Replace(capture)
	if(ikarus.iuc.delegate ~= nil) then
		local replacement = ikarus.iuc.replacements[string.upper(capture)];
		if(type(replacement) == "function") then
			return replacement(ikarus.iuc.delegate);
		elseif(replacement ~= nil) then
			return replacement;
		end;
	end;
end;


function ikarus.iuc.NAME(delegate)
	if(delegate:IsUnit()) then
		return tostring(UnitName(delegate.unit));
	end;
end;

function ikarus.iuc.LVL(delegate)
	if(delegate:IsUnit()) then
		local lvl, _ = UnitLevel(delegate.unit);
		if(lvl and lvl > 0) then
			return tostring(lvl);
		else
			return "-";
		end;
	end;
end;

function ikarus.iuc.SLVL(delegate)
	if(delegate:IsUnit()) then
		local _, lvl = UnitLevel(delegate.unit);
		if(lvl and lvl > 0) then
			return tostring(lvl);
		else
			return "-";
		end;
	end;
end;

function ikarus.iuc.CLASS(delegate)
	if(delegate:IsUnit()) then
		local class, _ = UnitClassToken(delegate.unit);
		if(class ~= "") then
			return ikarus.lang.units.classes[class];
		else
			return "-";
		end;
	end;
end;

function ikarus.iuc.SCLASS(delegate)
	if(delegate:IsUnit()) then
		local _, class = UnitClassToken(delegate.unit);
		if(class ~= "") then
			return ikarus.lang.units.classes[class];
		else
			return "-";
		end;
	end;
end;

function ikarus.iuc.CLASS_SYMBOL(delegate)
	if(delegate:IsUnit()) then
		local class, _ = UnitClassToken(delegate.unit);
		if(class ~= "") then
			return ikarus.lang.units.classes[class]:sub(1,1);
		else
			return "-";
		end;
	end;
end;

function ikarus.iuc.SCLASS_SYMBOL(delegate)
	if(delegate:IsUnit()) then
		local _, class = UnitClassToken(delegate.unit);
		if(class ~= "") then
			return ikarus.lang.units.classes[class]:sub(1,1);
		else
			return "-";
		end;
	end;
end;

function ikarus.iuc.HP(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			return ikarus.utils:FormatNumber(UnitHealth(delegate.unit), delegate:GetProperty("NUMBER_FORMAT"));
		else
			--ikarus.mobdb:UpdateMobDB(delegate.unit);
			local hpMax, hpRel = ikarus.mobdb:UnitMaxHealth(delegate.unit) or 100, UnitHealth(delegate.unit);
			return ikarus.utils:FormatNumber(math.floor(hpRel/100 * hpMax), delegate:GetProperty("NUMBER_FORMAT"));
		end;
	end;
end;

function ikarus.iuc.HP_MAX(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			return ikarus.utils:FormatNumber(UnitMaxHealth(delegate.unit), delegate:GetProperty("NUMBER_FORMAT"));
		else
			--ikarus.mobdb:UpdateMobDB(delegate.unit);
			return ikarus.utils:FormatNumber(ikarus.mobdb:UnitMaxHealth(delegate.unit) or 100, delegate:GetProperty("NUMBER_FORMAT"));
		end;
	end;
end;

function ikarus.iuc.HP_REL(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			local hp, hpMax = UnitHealth(delegate.unit), UnitMaxHealth(delegate.unit);
			return tostring(math.floor(hp/hpMax * 100));
		else
			return tostring(UnitHealth(delegate.unit));
		end;
	end;
end;

function ikarus.iuc.HEALTH(delegate)
	if(delegate:IsUnit()) then
		local hpRel, hp, hpMax;
		if(UnitIsPlayer(delegate.unit)) then
			hp = UnitHealth(delegate.unit);
			hpMax = UnitMaxHealth(delegate.unit);
			if(hpMax == 0) then
				return "";
			elseif(hp == 0) then
				hpRel = 0;
			else
				hpRel = math.floor(hp/hpMax * 100);
			end;
		else
			--ikarus.mobdb:UpdateMobDB(delegate.unit);
			hpRel = UnitHealth(delegate.unit);
			hpMax = ikarus.mobdb:UnitMaxHealth(delegate.unit) or 100;
			hp = math.floor(hpRel/100 * hpMax);
		end;
		return ikarus.utils:FormatNumber(hp, delegate:GetProperty("NUMBER_FORMAT")).." / "..ikarus.utils:FormatNumber(hpMax, delegate:GetProperty("NUMBER_FORMAT")).." ("..hpRel.."%)";
	end;
end;

function ikarus.iuc.HP_DIFF(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			return ikarus.utils:FormatNumber(UnitMaxHealth(delegate.unit) - UnitHealth(delegate.unit), delegate:GetProperty("NUMBER_FORMAT"));
		else
			--ikarus.mobdb:UpdateMobDB(delegate.unit);
			local hpRel, hpMax = 100 - UnitHealth(delegate.unit), ikarus.mobdb:UnitMaxHealth(delegate.unit) or 100;
			return ikarus.utils:FormatNumber(math.floor(hpMax/100 * hpRel), delegate:GetProperty("NUMBER_FORMAT"));		
		end;
	end;
end;

function ikarus.iuc.MP(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			return ikarus.utils:FormatNumber(UnitMana(delegate.unit), delegate:GetProperty("NUMBER_FORMAT"));
		else
			return tostring(UnitMana(delegate.unit));
		end;
	end;
end;

function ikarus.iuc.MP_MAX(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			return ikarus.utils:FormatNumber(UnitMaxMana(delegate.unit), delegate:GetProperty("NUMBER_FORMAT"));
		else
			return tostring(UnitMaxMana(delegate.unit));
		end;
	end;
end;

function ikarus.iuc.MP_REL(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			local mp, mpMax = UnitMana(delegate.unit), UnitMaxMana(delegate.unit);
			if(mp == 0) then
				return "0";
			else
				return tostring(math.floor(mp/mpMax * 100));
			end;
		else
			return tostring(UnitMana(delegate.unit));
		end;
	end;
end;

function ikarus.iuc.MANA(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			local mp, mpMax, mpRel = UnitMana(delegate.unit), UnitMaxMana(delegate.unit);
			if(mpMax == 0) then
				return "";
			elseif(mp == 0) then
				mpRel = 0;
			else
				mpRel = math.floor(mp/mpMax * 100);
			end;
			return ikarus.utils:FormatNumber(mp, delegate:GetProperty("NUMBER_FORMAT")).." / "..ikarus.utils:FormatNumber(mpMax, delegate:GetProperty("NUMBER_FORMAT")).." ("..mpRel.."%)"; 
		else
			return tostring(UnitMana(delegate.unit)).."%";
		end;
	end;
end;

function ikarus.iuc.MANA_TYPE(delegate)
	if(delegate:IsUnit()) then
		return ikarus.lang.units.manatypes["MANATYPE"..UnitManaType(delegate.unit)];
	end;
end;

function ikarus.iuc.SP(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			return ikarus.utils:FormatNumber(UnitSkill(delegate.unit), delegate:GetProperty("NUMBER_FORMAT"));
		else
			return tostring(UnitSkill(delegate.unit));
		end;
	end;
end;

function ikarus.iuc.SP_MAX(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			return ikarus.utils:FormatNumber(UnitMaxSkill(delegate.unit), delegate:GetProperty("NUMBER_FORMAT"));
		else
			return tostring(UnitMaxSkill(delegate.unit));
		end;
	end;
end;

function ikarus.iuc.SKILL(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsPlayer(delegate.unit)) then
			local sp, spMax, spRel= UnitSkill(delegate.unit), UnitMaxSkill(delegate.unit);
			if(spMax == 0) then
				return "";
			elseif(sp == 0) then
				spRel = 0;
			else
				spRel = math.floor(sp/spMax * 100);
			end;
			return ikarus.utils:FormatNumber(sp, delegate:GetProperty("NUMBER_FORMAT")).." / "..ikarus.utils:FormatNumber(spMax, delegate:GetProperty("NUMBER_FORMAT")).." ("..spRel.."%)"; 
		else
			return tostring(UnitSkill(delegate.unit)).."%";
		end;
	end;
end;

function ikarus.iuc.SKILL_TYPE(delegate)
	if(delegate:IsUnit()) then
		return ikarus.lang.units.manatypes["MANATYPE"..UnitSkillType(delegate.unit)];
	end;
end;

function ikarus.iuc.TYPE(delegate)
	if(delegate:IsUnit()) then
		local unitType;
		if(UnitIsPlayer(delegate.unit)) then
			unitType = "PLAYER";
		else
			if(UnitCanAttack("player",delegate.unit)) then
				local sex = UnitSex(delegate.unit);
				if(sex > 2) then
					unitType = "BOSS";
				elseif(sex > 1) then
					unitType = "ELITE";
				else
					unitType = "MOB";
				end;
			else
				unitType = "NPC";
			end;
		end;
		if(unitType) then
			return ikarus.lang.units.types[unitType];
		else
			return "-";
		end;
	end;
end;

function ikarus.iuc.TITLE(delegate)
	if(delegate:IsUnit()) then
		return UnitTitle(delegate.unit);
	end;
end;

function ikarus.iuc.STATE(delegate)
	if(delegate:IsUnit()) then
		if(UnitIsDeadOrGhost(delegate.unit)) then
			return ikarus.lang.units.states.DEAD;
		else
			return ikarus.lang.units.states.ALIVE;
		end;
	end;
end;

function ikarus.iuc.TARGET(delegate)
	if(delegate:IsUnit()) then
		if(delegate.unit == "player") then
			if(UnitExists("target")) then
				return UnitName("target");
			end;
		else
			if(UnitExists(delegate.unit .."target")) then
				return UnitName(delegate.unit .."target");
			end;
		end;
		return "-";
	end;
end;

function ikarus.iuc.UNIT(delegate)
	return ikarus.lang.config.units[delegate.unit];
end;

function ikarus.iuc.DISTANCE(delegate)
	if(delegate:IsUnit()) then
		return UnitDistance(delegate.unit);
	end;
end;

function ikarus.iuc.SPELL_NAME(delegate)
	if(delegate.isCastBar) then
		return delegate.spell or "";
	end;
end;

function ikarus.iuc.SPELL_CASTTIME(delegate)
	if(delegate.isCastBar) then
		if(delegate.duration) then
			return string.format("%.1f", delegate.duration);
		else
			return "";
		end;
	end;
end;

function ikarus.iuc.SPELL_DURATION(delegate)
	if(delegate.isCastBar) then
		if(delegate.startTime and delegate.stopTime) then
			return string.format("%.1f", delegate.stopTime - delegate.startTime);
		else
			return "";
		end;
	end;
end;

function ikarus.iuc.SPELL_LEFT(delegate)
	if(delegate.isCastBar) then
		if(delegate.startTime and delegate.spentTime) then
			return string.format("%.1f", delegate.stopTime - delegate.spentTime);
		else
			return "";
		end;
	end;
end;

function ikarus.iuc.SPELL_SPENT(delegate)
	if(delegate.isCastBar) then
		if(delegate.spentTime) then
			return string.format("%.1f", delegate.spentTime);
		else
			return "";
		end;
	end;
end;

function ikarus.iuc.SPELL_PROGRESS(delegate)
	if(delegate.isCastBar) then
		if(delegate.progress) then
			return math.floor(delegate.progress * 100);
		else
			return "";
		end;
	end;
end;

function ikarus.iuc.SPELL(delegate)
	if(delegate.isCastBar) then
		if(delegate.spell) then
			return string.format("%.1f", delegate.spentTime).." / "..string.format("%.1f", delegate.stopTime-delegate.startTime).."  ("..tostring(math.floor(delegate.progress*100)).."%)";
		else
			return "";
		end;
	end;
end;

function ikarus.iuc.PING(delegate)
	return tostring(math.floor(GetPing()));
end;

function ikarus.iuc.FPS(delegate)
	return string.format("%.1f", GetFramerate());
end;

function ikarus.iuc.ZONE_ID(delegate)
	return tostring(GetZoneID());
end;

function ikarus.iuc.ZONE(delegate)
	return tostring(GetZoneName());
end;